#define JMBC_INSIDE
#include "plParticle.h"
#include <Math/mlMath.h>
#include <Core/clCore.h>
#include <stdlib.h>
#include <GL/gl.h>

// Context

#define STORE ID_SMALL_STORE

static PLParticles *store[STORE];

void
plParticleCreateContext(void)
{
	idClearStore(PLParticles, STORE, store);
}

// Generation / Deletion

static
void
Init(PLParticles *particle)
{
}

static
void
Term(PLParticles *particle)
{
}

void
plGenParticles(unsigned int n, unsigned int *particles)
{
	idGenObjects(PLParticles, STORE, store, Init, n, particles);
}

void
plDeleteParticles(unsigned int n, unsigned int *particles)
{
	idDeleteObjects(store, Term, n, particles);
}

// Load / Unload

void
plParticlesLoad(unsigned int id, unsigned int n, unsigned int type)
{
	PLParticles *particles;

	particles = store[id];

	tlGenGroups(1, &particles->group);
	tlGroupAllocate(particles->group, n, sizeof(PLParticle));
}

void
plParticlesUnload(unsigned int id)
{
	PLParticles *particles;

	particles = store[id];

	tlGroupUnload(particles->group);
	tlDeleteGroups(1, &particles->group);
}

// Query

unsigned int
plParticlesLength(unsigned int id)
{
	PLParticles *particles;

	particles = store[id];

	return tlGroupLength(particles->group);
}

float *
plParticlesEnergy(unsigned int id, unsigned int particle)
{
	PLParticles *particles;

	particles = store[id];

	return &((PLParticle *) tlGroupArray(particles->group))[particle].energy;
}

float *
plParticlesMatrix(unsigned int id, unsigned int particle)
{
	PLParticles *particles;

	particles = store[id];

	return &((PLParticle *) tlGroupArray(particles->group))[particle].matrix[0];
}

void *
plParticlesData(unsigned int id, unsigned int particle)
{
	PLParticles *particles;

	particles = store[id];

	return &((PLParticle *) tlGroupArray(particles->group))[particle].data;
}

// Operation

void
plParticlesActuatorInitializer(unsigned int id, unsigned int particle, void *user)
{
	float *energy;
	float *matrix;

	energy = plParticlesEnergy(id, particle);
	matrix = plParticlesMatrix(id, particle);

	*energy = 0.0f;
	mlMatrixLoadIdentity(matrix);
}

void
plParticlesAct(unsigned int id,  void (*actuator)(unsigned int id, unsigned int particle, void *user), void *user)
{
	PLParticles *particles;
	unsigned int i;

	particles = store[id];

	for(i = 0; i < tlGroupLength(particles->group); i++)
		actuator(id, i, user);
}

static
int
plParticleDepthCmp(const void *a, const void *b)
{
	float mv[16], va[3], vb[3];

	glGetFloatv(GL_MODELVIEW_MATRIX, mv);
	mlVectorCopy(&((PLParticle *) a)->matrix[12], va);
	mlVectorMultMatrix(va, mv);
	mlVectorCopy(&((PLParticle *) b)->matrix[12], vb);
	mlVectorMultMatrix(vb, mv);

	return (int) (va[2] - vb[2]);
}

void
plParticlesDepthSort(unsigned int id)
{
	PLParticles *particles;

	particles = store[id];

	qsort(tlGroupArray(particles->group), tlGroupLength(particles->group), sizeof(PLParticle), plParticleDepthCmp);
}
